/*
 * hello_dt driver for vexpress on linux-4.19.27
*/
#include <linux/module.h>
 
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
 
#include <linux/slab.h>       //
#include <linux/io.h>
 
#include <linux/gfp.h>
#include <linux/cdev.h>
 
#include <linux/uaccess.h> /* copy_from_usr, */
 
 
/*
 dts:
 HelloWorld {
	compatible = "vexpress, hello_world";
	status = "okay";
 };
 
*/
 
 
 
#define DEV_NAME		"hello"
 
struct pri_dev
{	
	dev_t dev;
	int major;
	char name[32];
	struct cdev *cdev;
	struct class *class;
};
 
 
static struct pri_dev *dev = NULL;
 
 
static int beep_open (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "open \n");	
	
	return 0;
}
 
static int beep_close (struct inode *inode, struct file *file)
{
	printk(KERN_INFO "close \n");	
	
	return 0;
}
 
static ssize_t beep_write (struct file *file, const char __user *usrbuf, size_t len, loff_t *off)
{
	printk(KERN_INFO "write \n");	
	
	return 0;
}
 
static long beep_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
	printk(KERN_INFO "ioctl \n");	
 
	return 0;
}
 
 
const struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_close,
	.unlocked_ioctl = beep_ioctl,
	.write = beep_write,
};
 
 
static int chrdev_setup(void)
{
	int ret = 0;
	dev = kmalloc(sizeof(struct pri_dev),GFP_KERNEL);
	if(!dev)
		return -ENOMEM;
 
	strcpy(dev->name, DEV_NAME);
 
	ret = alloc_chrdev_region(&dev->dev, 0, 1, dev->name);
	dev->major = MAJOR(dev->dev);
	
	if(ret < 0)
	{
		kfree(dev);
		return ret;
	}
 
	dev->cdev = cdev_alloc();
	if(!dev->cdev)
	{
		unregister_chrdev_region(dev->dev,1);
		kfree(dev);
		return -EFAULT;
	}
	cdev_init(dev->cdev,&fops);
	dev->cdev->owner = THIS_MODULE;
	dev->cdev->ops = &fops;
	cdev_add(dev->cdev,dev->dev,1);
 
	dev->class = class_create(THIS_MODULE,dev->name);
	ret = PTR_ERR(dev->class);
	if (IS_ERR(dev->class))
	{
		cdev_del(dev->cdev);
		unregister_chrdev_region(dev->dev,1);
		kfree(dev);
		return -EFAULT;
	}
 
	device_create(dev->class,NULL,dev->dev,NULL,dev->name,dev);
 
	return 0;
}
 
 
static void chrdev_setdown(void)
{
	device_destroy(dev->class, dev->dev);
	class_destroy(dev->class);
	cdev_del(dev->cdev);
	unregister_chrdev_region(dev->dev,1);
	kfree(dev);
}
 
 
static int hello_world_probe(struct platform_device *pdev)
{
	int count = 0;
	int ret = 0;
	void *platdata = NULL;
 	unsigned int val=0;

	pr_notice("------------------- %s\n", __func__);
 
	platdata = dev_get_platdata(&pdev->dev);
	pr_notice("platdata = %p\n", platdata);
	pr_notice("device_tree = %p\n", pdev->dev.of_node);
	count = of_get_child_count(pdev->dev.of_node);
	pr_notice("child_count = %d\n", count);
 
	of_property_read_u32(pdev->dev.of_node, "name1", &val);

	pr_notice("%s,val: 0x%x\n",__func__,val);
	ret = chrdev_setup();
	if(ret){
		pr_err("chrdev setup error.\n");
		return -EINVAL;
	}
		
    return 0;
}
static int hello_world_remove(struct platform_device *pdev)
{
	pr_notice("------------------- %s\n", __func__);
 
	chrdev_setdown();
		
	return 0;
}
static void hello_world_shutdown(struct platform_device *pdev)
{
	pr_notice("------------------- %s\n", __func__);
	return ;
}
static int hello_world_suspend(struct platform_device *pdev, pm_message_t state)
{
	pr_notice("------------------- %s\n", __func__);
	return 0;
}
static int hello_world_resume(struct platform_device *pdev)
{
	pr_notice("------------------- %s\n", __func__);
	return 0;
}
 
 
 
static const struct of_device_id vexpress_hello_world_dt_match[] = {
        { .compatible = "kvim3, hello_world" },
        {},
};
 
 
static struct platform_driver vexpress_hello_dt_drv = 
{
	.probe          = hello_world_probe,
	.remove         = hello_world_remove,
    .shutdown   = hello_world_shutdown,
    .suspend    = hello_world_suspend,
    .resume     = hello_world_resume, 
    .driver         = {
            .name   = "hello world",
            .of_match_table = vexpress_hello_world_dt_match,
    },
};
 
module_platform_driver(vexpress_hello_dt_drv);
 
MODULE_LICENSE("GPL");
